package com.zbu.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.zbu.common.core.constants.Constants;
import com.zbu.common.core.enums.ResultCode;
import com.zbu.common.security.exception.ServiceException;
import com.zbu.system.domain.exam.Exam;
import com.zbu.system.domain.exam.ExamQuestion;
import com.zbu.system.domain.exam.dto.ExamAddDTO;
import com.zbu.system.domain.exam.dto.ExamEditDTO;
import com.zbu.system.domain.exam.dto.ExamQueryDTO;
import com.zbu.system.domain.exam.dto.ExamQuestAddDTO;
import com.zbu.system.domain.exam.vo.ExamDetailVO;
import com.zbu.system.domain.exam.vo.ExamVO;
import com.zbu.system.domain.question.Question;
import com.zbu.system.domain.question.vo.QuestionVO;
import com.zbu.system.manager.ExamCacheManager;
import com.zbu.system.mapper.exam.ExamMapper;
import com.zbu.system.mapper.exam.ExamQuestionMapper;
import com.zbu.system.mapper.question.QuestionMapper;
import com.zbu.system.service.exam.IExamService;
import com.zbu.system.service.question.impl.QuestionServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {
    @Resource
    private ExamMapper examMapper;
    @Resource
    private QuestionMapper questionMapper;
    @Resource
    private ExamQuestionMapper examQuestionMapper;
    @Autowired
    private ExamCacheManager examCacheManager;
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());
        return examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public int  add(ExamAddDTO examAddDTO) {
        checkExamSaveParms(examAddDTO);
        Exam exam=new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);
        return examMapper.insert(exam);

    }

    @Override
    public boolean questionAdd(ExamQuestAddDTO eXamQuestionAddDTO) {
        Exam exam = getExam(eXamQuestionAddDTO.getExamId());
        checkExam(exam);
        Set<Long>questionIdSet=eXamQuestionAddDTO.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIdSet)){
            return false;
        }
        List<Question>questionList=questionMapper.selectBatchIds(questionIdSet);
        if(questionList==null||questionList.size()<questionIdSet.size()){
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        return saveExamQuestion(exam, questionIdSet);
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO=new ExamDetailVO();
        Exam exam=getExam(examId);
        BeanUtil.copyProperties(exam,examDetailVO);
        List<ExamQuestion> examQuestions= examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        if(CollectionUtil.isEmpty(examQuestions)){
                return examDetailVO;
        }
        List<Long>questionIdList=examQuestions.stream().map(ExamQuestion::getQuestionId).toList();
        List<Question>questionList=questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId,Question::getTitle,Question::getDifficulty)
                .in(Question::getQuestionId,questionIdList));
        List<QuestionVO>questionVOList=BeanUtil.copyToList(questionList,QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int  edit(ExamEditDTO examEditDTO) {
       checkExamSaveParms(examEditDTO,examEditDTO.getExamId());
       Exam exam=getExam(examEditDTO.getExamId());
       if(Constants.TRUE.equals(exam.getStatus())){
           throw  new ServiceException(ResultCode.EXAM_IS_PUBLISH);
       }
       checkExam(exam);
       exam.setTitle(examEditDTO.getTitle());
       exam.setStartTime(examEditDTO.getStartTime());
       exam.setEndTime(examEditDTO.getEndTime());
       return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam=getExam(examId);
        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));

    }

    @Override
    public int delete(Long examId) {
        Exam exam=getExam(examId);
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(examId);
    }

    @Override
    public int publish(Long examId) {
        Exam exam=getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        Long count=examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        if(count==null||count<=0){
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);

        }
        exam.setStatus(Constants.TRUE);
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);

    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam=getExam(examId);
        checkExam(exam);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    private static void checkExam(Exam exam) {
        if(exam.getStartTime().isBefore(LocalDateTime.now())){
            throw  new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private boolean saveExamQuestion(Exam exam, Set<Long> questionIdSet) {
        List<ExamQuestion>examQuestions=new ArrayList<>();
        int num=1;
        for(Long questionId: questionIdSet){
            ExamQuestion examQuestion=new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestions.add(examQuestion);
        }
        return saveBatch(examQuestions);
    }

    private Exam getExam(Long examId) {
        Exam exam=examMapper.selectById(examId);
        if(exam==null){
            throw  new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return exam;
    }
    private void checkExamSaveParms(ExamAddDTO examSaveDTO) {
        List<Exam>exams=examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examSaveDTO.getTitle()));
        if(CollectionUtil.isNotEmpty(exams)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if(examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);//竞赛时间不能早于当前时间
        }
        if(examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())){
            throw  new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);//竞赛时间不能晚于竞赛结束时间
        }
    }
    private void checkExamSaveParms(ExamAddDTO examSaveDTO,Long examId) {
        List<Exam>exams=examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examSaveDTO.getTitle())
                .ne(Exam::getExamId,examId)
        );
        if(CollectionUtil.isNotEmpty(exams)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if(examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);//竞赛时间不能早于当前时间
        }
        if(examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())){
            throw  new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);//竞赛时间不能晚于竞赛结束时间
        }
    }
}
